package main

import (
	"fmt"
	"math/rand"
	"time"
)

// ==========数组==========
// 1、数组可以存放多个同一类型的数据, 在golang中数组是值类型
// 2、数组的长度可以用len(array)来获取
// 3、数组是多个相同类型数据的集合, 一个数组一旦声明/定义了, 其长度是固定的, 不能动态变化
// 4、数组中的元素可以是任意类型, 既可以是值类型也可以是引用类型
// 5、数组创建后, 如果没有赋值, 有默认值, 数值(int、float系列)数组默认值是0, 字符串数组默认是"", bool数组默认是false
// 6、数组下标必须在指定范围内使用, 否则报panic

type Student struct {
	Age   int
	Marry bool
}

// 数组的声明、定义
func DefineArray() {
	// 声明一个数组, 数据类型是整数
	// 声明后数组里3个整数类型的默认零值0
	var intArray [3]int
	fmt.Println(intArray) // [0 0 0]

	// 声明一个数组, 数据类型是浮点数
	// 声明后数组里3个浮点数类型的默认零值0
	var floatArray [3]float64
	fmt.Println(floatArray) // [0 0 0]

	// 声明一个数组, 数据类型是字符串
	// 声明后数组里3个字符串类型的默认零值空字符串
	var stringArray [3]string
	fmt.Println(stringArray) // [  ]

	// 声明一个数组, 数据类型是布尔类型
	// 声明后数组里3个布尔类型的默认零值false
	var boolArray [3]bool
	fmt.Println(boolArray) // [false false false]

	// 声明一个数组, 数据类型是error类型
	// 声明后数组里3个error类型的默认零值<nil>
	var errorArray [3]error
	fmt.Println(errorArray) // [<nil> <nil> <nil>]

	// 声明一个数组, 数据类型是Student结构体类型
	// 声明后数组里3个Student结构体类型的默认零值{0 false}
	var structArray [3]Student
	fmt.Println(structArray) // [{0 false} {0 false} {0 false}]
}

// 数组的赋值
func AssignArray() {
	var intArray [3]int
	intArray[0] = 10
	intArray[1] = 20
	intArray[2] = 30
	// intArray[3] = 40  错误赋值, 超出来数组的长度
	fmt.Println(intArray) // [10 20 30]
}

// 数组初始化的方式
func InitArray() {
	// 1、声明并赋值
	var arr1 [3]int = [3]int{1, 2, 3}
	fmt.Println(arr1) // [1 2 3]

	// 2、不用声明数组类型, 类型推导
	var arr2 = [3]int{1, 2, 3}
	fmt.Println(arr2) // [1 2 3]

	// 3、...是固定写法, 可以初始化任意多个元素
	var arr3 = [...]int{1, 2, 3, 4, 5}
	fmt.Println(arr3) // [1 2 3 4 5]

	// 4、指定下标并赋值
	var arr4 = [...]int{1: 8, 0: 6, 3: 10}
	fmt.Println(arr4) // [6 8 0 10]
	var arr5 = [...]string{1: "max", 0: "allen"}
	fmt.Println(arr5) // [allen max]
	var arr6 = [5]float64{0: 1, 3: 2}
	fmt.Println(arr6) //[1 0 0 2 0]
}

// 数组内存地址
func ArrayAddress() {
	// 数组的地址可以通过数组名来获取, &intArr
	// 数组中第一个元素的地址就是数组的地址
	// 下一个元素的地址等于前一个元素的地址加上数组中元素类型占用的字节数, 这边int就是8个字节
	var intArr [3]int
	fmt.Printf("intArr的地址%p\n", &intArr)         // intArr的地址0xc000018180
	fmt.Printf("intArr第一个元素的地址%p\n", &intArr[0]) // intArr第一个元素的地址0xc000018180
	fmt.Printf("intArr第二个元素的地址%p\n", &intArr[1]) // intArr第二个元素的地址0xc000018188
	fmt.Printf("intArr第三个元素的地址%p\n", &intArr[2]) // intArr第三个元素的地址0xc000018190
}

// 数组的遍历
func TravelArray() {
	var arr = [...]int{1: 8, 0: 6, 3: 10}

	// 1、常规遍历
	for i := 0; i < len(arr); i++ {
		fmt.Println(arr[i])
	}

	// for range遍历, index、value都是仅在for循环内部可见的局部变量
	// 如果不想使用下标index, 可以把下标index用_表示, 忽略下标
	// index、value不是固定的, 也可以用其他的名字表示
	for index, value := range arr {
		fmt.Println(index, value)
	}
}

// 数组是值类型, 不会影响函数外的数组, 相当于是拷贝一份新的数组, 因此函数内外的数组地址也是不一样的
func UpdateArray(arr [3]int) {
	arr[0] = 10
	fmt.Println("UpdateArray(), arr =", arr)             // UpdateArray(), arr = [10 2 3]
	fmt.Printf("UpdateArray(), arr address =%p\n", &arr) // UpdateArray(), arr address =0xc000018220
}

// 通过传入数组的地址来修改数组
func ModifyArray(arr *[3]int) {
	(*arr)[0] = 10                                      // 一定要先用*取出arr的值, 并用()括起来, 然后再修改元素的值
	fmt.Println("ModifyArray(), arr =", *arr)           // ModifyArray(), arr = [10 2 3]
	fmt.Printf("ModifyArray(), arr address =%p\n", arr) // ModifyArray(), arr address =0xc000018200
}

// 找出数组中最大的数及它的索引
func MaxValueInArray(arr [10]int) (int, int) {
	maxValueIndex := 0
	maxValue := arr[0]
	for i := 1; i < len(arr); i++ {
		if arr[i] > maxValue {
			maxValue = arr[i]
			maxValueIndex = i
		}
	}
	return maxValueIndex, maxValue
}

// 反转数组
func ReverseArray(arr [5]int) [5]int {
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < len(arr)/2; i++ {
		arr[i], arr[len(arr)-i-1] = arr[len(arr)-i-1], arr[i]
	}
	return arr
}

// 二维数组
func TwoDimArray() {
	// 二维数组的声明、定义
	var arr [2][3]int
	// 二维数组的遍历
	for row, v := range arr {
		for col, k := range v {
			fmt.Println(row, col, k)
		}
	}

	fmt.Printf("arr的地址是%p\n", &arr)          // arr的地址是0xc00001a210
	fmt.Printf("第一个数组的地址是%p\n", &arr[0])     // 第一个数组的地址是0xc00001a210
	fmt.Printf("第一个数组的首地址是%p\n", &arr[0][0]) // 第一个数组的首地址是0xc00001a210
	fmt.Printf("第二个数组的地址是%p\n", &arr[1])     // 第二个数组的地址是0xc00001a228
	fmt.Printf("第二个数组的首地址是%p\n", &arr[1][0]) // 第二个数组的首地址是0xc00001a228
	/*
			二维数组内存分析

			第二个数组的地址比第一个数组的地址大24个字节, 因为每一个数组里有3个int类型的整数, 而一个int类型的整数占用8个字节
		               0xc00001a210
			          -----------------------------
			arr ---> | 0xc00001a210 | 0xc00001a228 |
		              -----------------------------
			          |                |
			          V                V
			         0xc00001a210      0xc00001a228
			         -----------       ------------
			        | 0 | 0 | 0 |     | 0 | 10 | 0 |
			         -----------       ------------
	*/

	// 二维数组直接初始化
	var arr2 = [2][3]int{{1, 2, 3}, {4, 5, 6}}
	var arr3 = [...][3]int{{11, 22, 33}, {44, 55, 66}} // ...只能写在左边
	fmt.Println(arr2)                                  // [[1 2 3] [4 5 6]]
	fmt.Println(arr3)                                  // [[11 22 33] [44 55 66]]
}

func main() {
	DefineArray()
	AssignArray()
	InitArray()
	ArrayAddress()
	TravelArray()

	arr := [3]int{1, 2, 3}
	UpdateArray(arr)
	fmt.Println("main(), arr =", arr)             // main(), arr = [1 2 3]
	fmt.Printf("main(), arr address =%p\n", &arr) // main(), arr address =0xc000018200
	ModifyArray(&arr)
	fmt.Println("main(), arr =", arr)             // main(), arr = [1 2 3]
	fmt.Printf("main(), arr address =%p\n", &arr) // main(), arr address =0xc000018200

	MaxValueInArray([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

	intArr := [5]int{1, 2, 3, 4, 5}
	intArr = ReverseArray(intArr)
	fmt.Println(intArr) // [5 4 3 2 1]

	TwoDimArray()
}
